package com.xbsj.server.service.Impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xbsj.server.config.ServiceConfig;
import org.springframework.util.ObjectUtils;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

public class ModelSerTaskApp {

    static Map<String,String> fieldType = new HashMap<>();

    static Map<String,FileWriter> logWriters = new HashMap<>();
    /**
     * Modelser服务端地址
     */
    static String serverAddr = "http://localhost:";

    static String token = "";
    static ObjectMapper objectMapper = new ObjectMapper();
    private static Map<String,Object> post(String urlPath, Object params) {
        return request(urlPath, params, "POST");
    }
    private static Map<String,Object> get(String urlPath,Object params) {
        return request(urlPath,params,"GET");
    }
    private static Map<String,Object> request(String apiUrl, Object params,String method) {
        Map<String, Object> res = new HashMap<>();

        try {
            // 构建 URL 对象
            URL url = new URL(apiUrl);

            // 打开连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            // 设置请求方法为 POST
            connection.setRequestMethod(method);

            // 允许写入数据
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("authorization", token);
            if (!ObjectUtils.isEmpty(params)) {
                // 构建请求参数，根据接口要求构建合适的参数
                String requestBody = objectMapper.writeValueAsString(params);

                // 获取输出流
                try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
                    wr.write(requestBody.getBytes(Charset.forName("UTF-8")));
                    wr.flush();
                }
            }


            // 获取响应
            int responseCode = connection.getResponseCode();
            if(responseCode == 500){
                byte[] bytes = connection.getErrorStream().readAllBytes();
                String str = new String(bytes);
                res = objectMapper.readValue(bytes, HashMap.class);
            }else {
                byte[] bytes = connection.getInputStream().readAllBytes();
                String str = new String(bytes);
                res = objectMapper.readValue(bytes, HashMap.class);
            }


            // 关闭连接
            connection.disconnect();
            return res;
        }catch (java.net.ConnectException e){
            res.put("status","CIMRTS服务连接失败，CIMRTS下载地址：https://bjxbsj.cn/cimrts.html");
            return res;
        }
        catch (IOException e) {
            e.printStackTrace();
            return res;
        }
    }


    private static String login(String port, String username,String password) {
        String url = serverAddr + port + "/system/user/login";
        Map<String, Object> payload = new HashMap<>();
        payload.put("username", username);
        payload.put("password", password);

        Map<String,Object> dataset = post(url, payload);
        if (!"ok".equals(dataset.get("status"))) {
            throw new RuntimeException((String)dataset.get("status"));
        }
        return (String)dataset.get("data");
    }

    /**
     * 创建数据
     * @param port modelser服务器端口
     * @param datasetName 数据集名称，自定义，但不能重复
     * @param srs 空间参考，例：EPSG:4547
     * @param file_path 存储路径
     * @param projh
     * @param folder
     * @return
     */
    private static Map<String,Object>  createDataset(String port, String datasetName,String srs,String file_path,Integer projh,Integer folder) {
        String url = serverAddr + port + "/ds/record/create";
        Map<String, Object> payload = new HashMap<>();
        payload.put("datasetName", datasetName); // 数据集名称
        payload.put("srs", srs);// 空间参考
        payload.put("filepath", file_path); // 存储文件夹名称
        payload.put("projh", projh); // 投影高
        payload.put("folder", folder); // 文件夹索引

        Map<String,Object> dataset = post(url, payload);
        if (!"ok".equals(dataset.get("status"))) {
            throw new RuntimeException((String)dataset.get("status"));
        }
        return dataset;
    }

    private static Map<String,Object>  importOsgb(String port, String datasetName,String file_path,List<Double> zeroPoint) {
        String url = serverAddr + port + "/system/task/import/osgb";
        Map<String, Object> param = new HashMap<>();
        param.put("folder", file_path);
        param.put("zeroPoint", zeroPoint);// 空间参考

        Map<String, Object> payload = new HashMap<>();
        payload.put("dataset", datasetName); // 数据集名称
        payload.put("type", "OsgbImporter");
        payload.put("param", param);

        Map<String,Object> dataset = post(url, payload);
        if (!"ok".equals(dataset.get("status"))) {
            throw new RuntimeException((String)dataset.get("status"));
        }
        return (Map)dataset.get("data");
    }

    private static Map<String,Object>  mergetop(String port, String tileservice) {
        String url = serverAddr + port + "/system/task/mergetop";
        Map<String, Object> payload = new HashMap<>();
        payload.put("tileservice", tileservice);
        Map<String,Object> dataset = post(url, payload);
        if (!"ok".equals(dataset.get("status"))) {
            throw new RuntimeException((String)dataset.get("status"));
        }
        return (Map)dataset.get("data");
    }

    private static Map<String,Object>  export3dtiles(String port, String tileservice,String path) {
        String url = serverAddr + port + "/system/task/export3dtiles";
        Map<String, Object> output = new HashMap<>();
        output.put("path", path);
        output.put("type", "file");

        Map<String, Object> param = new HashMap<>();
        param.put("doubleSided", false);
        param.put("draco", true);
        param.put("ktx2", true);
        param.put("noLight", true);
        param.put("draco", true);
        param.put("output", output);
        param.put("version11", true);

        Map<String, Object> payload = new HashMap<>();
        payload.put("tileservice", tileservice);
        payload.put("param", param);

        Map<String,Object> dataset = post(url, payload);
        if (!"ok".equals(dataset.get("status"))) {
            throw new RuntimeException((String)dataset.get("status"));
        }
        return (Map)dataset.get("data");
    }

    // 创建切片服务
    private static Map<String,Object>  createTileservice(String port, String tileServiceName, String filepath, List<String> datasetName) {
        String url = serverAddr + port + "/ts/record/create";
        Map<String, Object> payload = new HashMap<>();
        payload.put("tileServiceName", tileServiceName);// 切片服务名称
        payload.put("filepath", filepath); // 存储文件夹名称
        payload.put("datasetName", datasetName); // 数据集名称。数组

        final Map<String, Object> response = post(url, payload);
        if (!"ok".equals(response.get("status"))) {
            throw new RuntimeException((String)response.get("status"));
        }
        return (Map<String,Object>) response.get("data");
    }

    // 查看正在执行的任务
    private static List<Map<String,Object>>  runningTask(String port) {
        String url = serverAddr + port + "/system/task/taskqueue";
        final Map<String, Object> stringObjectMap = get(url, null);
        if(ObjectUtils.isEmpty(stringObjectMap)){
            return new ArrayList<>();
        }
        return (List<Map<String,Object>>) stringObjectMap.get("data");
    }

    private static List<Map<String,Object>>  getFinishDatasetTask(String port,String dataset) {
        String url = serverAddr + port + "/system/task/finish/list";
        Map<String, Object> payload = new HashMap<>();
        payload.put("dataset", dataset);// 切片服务名称
        final Map<String, Object> post = post(url, payload);
        Map<String, Object> data = (Map<String, Object>)post.get("data");
        if(ObjectUtils.isEmpty(data)){
            return new ArrayList<>();
        }
        return  (List<Map<String, Object>>)data.get("data");
    }

    private static List<Map<String,Object>>  getFinishTileServiceTask(String port,String tileservice) {
        String url = serverAddr + port + "/system/task/finish/list";
        Map<String, Object> payload = new HashMap<>();
        payload.put("dataset", tileservice);// 切片服务名称
        final Map<String, Object> post = post(url, payload);
        Map<String, Object> data = (Map<String, Object>)post.get("data");
        if(ObjectUtils.isEmpty(data)){
            return new ArrayList<>();
        }
        return  (List<Map<String, Object>>)data.get("data");
    }

    private static void stopTask(String port,String taskId) {
        String url = serverAddr + port + "/system/task/stoptask?id="+taskId;
        final Map<String, Object> post = get(url, null);
//        if (!"ok".equals(post.get("status"))) {
//            throw new RuntimeException((String)post.get("status"));
//        }
//        if (!"ok".equals(post.get("status"))) {
//            throw new RuntimeException((String)post.get("status"));
//        }
//        Map<String, Object> data = (Map<String, Object>)post.get("data");
//        return  (List<Map<String, Object>>)data.get("data");
    }


    private static void removeTask(String port,String taskId) {
        String url = serverAddr + port + "/system/task/removetask?id="+taskId;
        final Map<String, Object> post = get(url, null);
//        if (!"ok".equals(post.get("status"))) {
//            throw new RuntimeException((String)post.get("status"));
//        }
//        Map<String, Object> data = (Map<String, Object>)post.get("data");
//        return  (List<Map<String, Object>>)data.get("data");
    }

    /**
     * 创建日志，不是必须的
     * @param taskId
     * @param log
     */
    private static void createLog(String taskId,String log){
        FileWriter logWriterFile;
        try {
            File logFile = new File(log);
            logWriterFile = new FileWriter(logFile,true);
            logWriters.put(taskId,logWriterFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    private static void writeLog(String taskId, Object log){
        if(ObjectUtils.isEmpty(log)){
            return;
        }
        try {
            FileWriter fileWriter = logWriters.get(taskId);
            fileWriter.write(objectMapper.writeValueAsString(log) + "\n");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void closeLog(String taskId){
        try {
            FileWriter fileWriter = logWriters.get(taskId);
            fileWriter.flush();
            fileWriter.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void stopTask(String port, String taskId, List<String> modelserTaskList) {
        modelserTaskList.stream().forEach(t -> {
            ModelSerTaskApp.stopTask(port,t);
            ModelSerTaskApp.removeTask(port,t);
        });
        closeLog(taskId);
    }


    //获取进度
    public static Map<String,Object> getProcess(String port, String taskId, List<String> modelserTaskList){
        Map<String,Object> result = new HashMap<>();
        final List<Map<String, Object>> runningTask = ModelSerTaskApp.runningTask(port);
        final List<Map<String, Object>> finishDatasetTask = ModelSerTaskApp.getFinishDatasetTask(port,taskId);
        final List<Map<String, Object>> finishTileServiceTask = ModelSerTaskApp.getFinishTileServiceTask(port,taskId);
        runningTask.addAll(finishDatasetTask);
        runningTask.addAll(finishTileServiceTask);

        double process = 0.0;
        boolean finished = true;

        for(String subTaskId: modelserTaskList){
            final List<Map<String, Object>> subTasks = runningTask.stream().filter(t -> {
                return t.get("id").equals(subTaskId);
            }).collect(Collectors.toList());
            if(subTasks.size() > 0){
                final Map<String, Object> subTask = subTasks.get(0);
                if(!ObjectUtils.isEmpty(subTask.get("running"))){
                    process += ((Double) subTask.get("progress")) / modelserTaskList.size();
                    finished = false;
                    break;
                }else{
                    writeLog(taskId,"failed: " + subTask);
                    if(!ObjectUtils.isEmpty(subTask.get("error"))){
                        modelserTaskList.stream().forEach(t -> {
                            ModelSerTaskApp.stopTask(port,t);
                            ModelSerTaskApp.removeTask(port,t);
                        });
                        result.put("status","error");
                        closeLog(taskId);
                        return result;
                    }
                    process += 1.0 / modelserTaskList.size();
                }
            } else{
                process += 1.0 / modelserTaskList.size();
            }
        };
        if(finished) {
            writeLog(taskId,"finished");
            closeLog(taskId);
            result.put("status", "finished");
            return result;
        }else{
            result.put("status", "running");
            result.put("percent", process);
        }
        return result;
    }

    public static void main(String[] argv) {
        fieldType.put("string", "String");
        fieldType.put("double", "Double");
        fieldType.put("int32_t", "Int32");
        fieldType.put("bool", "Boolean");
        fieldType.put("float", "Float");
//        String taskjson = "{\"port\":9009,\"inputs\":[{\"srs\":\"EPSG:4547\",\"type\":\"ClmImporter\",\"file\":\"D:/data/中文.clm\"}],\"output\":{\"folder\":\"D:\\\\data\\\\aa\"},\"TextureCompress\":{\"condition\":{}},\"LodCalculate\":{\"condition\":{},\"minPixel\": 8},\"CaculTilesStatus\":{\"maxZoom\":18},\"featureselector\":{\"minPixel\":8,\"maxzoom\":20,\"srcDataAtMaxZoom\":true},\"otherProperty\":[{\"name\":\"Height\",\"type\":\"String\"}],\"tscConfig\":{\"textureRatio\":4,\"draco\":true,\"textureFormat\":\"ktx2\"}}";

        List<String> modelserTaskList = new ArrayList<>();
        String taskId = argv[0];
        final String taskjson = argv[1];
        Map<String, Object> taskParam = null;
        try {
            taskParam = objectMapper.readValue(taskjson, HashMap.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        String port = taskParam.get("port").toString();
        final String outputFolder = (String) taskParam.get("output");

        token = login(port, "admin", "admin1234");

        final Map<String, Object> input = (Map) taskParam.get("input");
        String srs = (String) input.get("srs");
        // 依次调用接口。参数怎么传，要看CIMRTS接口文档，目前脚本默认填的数据集名称为ds2，切片服务名称为ts2。运行该脚本前，需要保证没有正在运行的任务。
        // 1.创建数据集
        // 2.数据导入
        // 3.创建切片服务
        // 4.数据导出

        //创建日志，可跳过
        String log = ServiceConfig.LabPath + File.separator + "logs" + File.separator + "Modelser.INFO." + taskId + ".log";
        createLog(taskId, log);

        //创建数据集存储路径
        String tempPath = ServiceConfig.LabPath + File.separator + "tasks" + File.separator + taskId;
        File dsDir = new File(tempPath, "ds");
        dsDir.mkdirs();

        //创建数据集
        try {
            Map<String, Object> dataset = createDataset(port, taskId, srs, dsDir.getAbsolutePath(), 0, 0);
            writeLog(taskId, "create dataset " + taskId + " success");
        } catch (Exception e) {
            e.printStackTrace();
            writeLog(taskId, "create dataset " + taskId + " error: " + e.getMessage());
            closeLog(taskId);
            throw new RuntimeException(e.getMessage());
        }

        final List<Double> srsorigin = Arrays.stream(input.get("srsorigin").toString().split(",")).map(o -> {
            return Double.parseDouble(o);
        }).collect(Collectors.toList());

        try {
            Map<String, Object> importResponse = importOsgb(port, taskId, input.get("folder").toString(), srsorigin);
            String id = importResponse.get("id").toString();
            modelserTaskList.add(id);
            writeLog(taskId, "create import osgb task: " + importResponse);
        } catch (Exception e) {
            e.printStackTrace();
            writeLog(taskId, "create import osgb task error: " + e.getMessage());
            closeLog(taskId);
            throw new RuntimeException(e.getMessage());
        }

        List<String> dsList = new ArrayList<String>();
        dsList.add(taskId);
        File tsDir = new File(tempPath, "ts");
        tsDir.mkdirs();

        try {
            Map<String, Object> tileServiceResponse = createTileservice(port, taskId, tsDir.getAbsolutePath(), dsList);
            String task = (String) tileServiceResponse.get("id");
            writeLog(taskId, "create tile server: " + tileServiceResponse);
        } catch (Exception e) {
            e.printStackTrace();
            writeLog(taskId, "create tile server error: " + e.getMessage());
            closeLog(taskId);
            throw new RuntimeException(e.getMessage());
        }

        try {
            Map<String, Object> response = mergetop(port, taskId);
            String mergetopid = (String) response.get("id");
            modelserTaskList.add(mergetopid);
            writeLog(taskId, "ceate cacul tiles status task: " + response);
        } catch (Exception e) {
            e.printStackTrace();
            writeLog(taskId, "ceate cacul tiles status task error: " + e.getMessage());
            closeLog(taskId);
            throw new RuntimeException(e.getMessage());
        }

        try {
            Map<String, Object> export3dtilesResponse = export3dtiles(port, taskId, taskParam.get("output").toString());
            String export3dtilesResponseid = (String) export3dtilesResponse.get("id");
            modelserTaskList.add(export3dtilesResponseid);
            writeLog(taskId, "create export3dtiles task: " + export3dtilesResponse);
        } catch (Exception e) {
            e.printStackTrace();
            writeLog(taskId, "create export3dtiles task error: " + e.getMessage());
            closeLog(taskId);
            throw new RuntimeException(e.getMessage());
        }
        
        while (true){
            try{
                final Map<String, Object> process = getProcess(port, taskId, modelserTaskList);
                System.out.println(process);
                if(!"running".equals(process.get("status"))){
                    break;
                }
                Thread.sleep(5000);
            }catch (Exception e){
                e.printStackTrace();
                break;
            }
        }
    }
}
